package com.closer.server.biz.problem.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.closer.server.biz.codebox.service.JudgeService;
import com.closer.server.biz.problem.dto.*;
import com.closer.server.biz.problem.dto.vo.AdminProblemVO;
import com.closer.server.biz.problem.dto.vo.ProblemVO;
import com.closer.server.biz.problem.entity.Submission;
import com.closer.server.biz.problem.entity.TestCase;
import com.closer.server.biz.problem.service.SubmissionService;
import com.closer.server.biz.problem.service.TestCaseService;
import com.closer.server.biz.problem.validator.ProblemValidator;
import com.closer.server.mapper.problem.TestCaseMapper;
import com.closer.server.mapper.user.UserStatisticsMapper;
import com.closer.server.support.exception.ErrorCode;
import com.closer.server.support.exception.ThrowUtils;
import com.google.gson.Gson;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.closer.server.biz.problem.entity.Problem;
import com.closer.server.mapper.problem.ProblemMapper;
import com.closer.server.biz.problem.service.ProblemService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * 题目表 服务层实现。
 *
 * @author closer
 */
@Service
public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem>  implements ProblemService{

    private final ProblemValidator validator;

    private final TestCaseService caseService;

    private final JudgeService judgeService;

    private final SubmissionService submissionService;

    private final TestCaseMapper testCaseMapper;

    private final UserStatisticsMapper userStatisticsMapper;

    private final Gson gson;


    public ProblemServiceImpl(ProblemValidator validator, TestCaseService caseService, Gson gson,TestCaseMapper testCaseMapper,
                              SubmissionService submissionService, UserStatisticsMapper userStatisticsMapper,JudgeService judgeService) {
        this.validator = validator;
        this.caseService = caseService;
        this.gson = gson;
        this.testCaseMapper = testCaseMapper;
        this.submissionService = submissionService;
        this.userStatisticsMapper = userStatisticsMapper;
        this.judgeService = judgeService;
    }

    @Override
    @Transactional
    public long add(AddRequest params) {
        validator.validate(params);
        var problem = Problem.builder()
                .title(params.getTitle())
                .description(params.getDescription())
                .difficulty(params.getDifficulty())
                .judgeConfig(gson.toJson(params.getJudgeConfig()))
                .inputDescription(params.getInputDescription())
                .outputDescription(params.getOutputDescription())
                .sampleInput(params.getSampleInput() != null ? params.getSampleInput() : null)
                .sampleOutput(params.getSampleOutput() != null ? params.getSampleOutput() : null)
                .hint(params.getHint()!= null ? params.getHint() : null)
                .createdUserId(params.getCreatedUserId())
                .build();
        this.save(problem);
        var testCases = TestCase.builder()
                .problemId(problem.getId())
                .testCase(gson.toJson(params.getJudgeCases()))
                .build();
        var result = caseService.save(testCases); 
        ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR,"测试样例数据库操作失败");
        return problem.getId();
    }

    @Override
    @Transactional
    public boolean delete(Long id) {
        validator.validateExitProblem(id);
        var result = this.removeById(id);
        ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR,"题目数据库操作失败");
        var testCasesId = testCaseMapper.getByProblemId(id);
        var testCasesResult = caseService.removeById(testCasesId);
        ThrowUtils.throwIf(!testCasesResult, ErrorCode.SYSTEM_ERROR,"测试样例数据库操作失败");
        return true;
    }

    @Override
    @Transactional
    public boolean updateProblem(UpdateRequest params) {
        validator.validate(params);
        var result = this.updateById(Problem.builder()
                .id(params.getProblemId())
                .title(params.getTitle())
                .description(params.getDescription())
                .difficulty(params.getDifficulty())
                .judgeConfig(gson.toJson(params.getJudgeConfig()))
                .inputDescription(params.getInputDescription())
                .outputDescription(params.getOutputDescription())
                .sampleInput(params.getSampleInput() != null ? params.getSampleInput() : null)
                .sampleOutput(params.getSampleOutput() != null ? params.getSampleOutput() : null)
                .hint(params.getHint()!= null ? params.getHint() : null)
                .build());
        ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR,"题目数据库操作失败");
        var testCasesId = testCaseMapper.getByProblemId(params.getProblemId());
        var testCasesResult = caseService.updateById(TestCase.builder()
                .id(testCasesId)
                .problemId(params.getProblemId())
                .testCase(gson.toJson(params.getJudgeCases()))
                .build());
        ThrowUtils.throwIf(!testCasesResult, ErrorCode.SYSTEM_ERROR,"测试样例数据库操作失败");
        return true;
    }

    @Override
    public Page<ProblemVO> search(SearchRequest params) {
        validator.validate(params);
        return this.mapper.getProblemVOList(params);
    }

    @Override
    public Page<AdminProblemVO> adminSearch(SearchByAdminRequest params) {
        validator.validate(params);
        return this.mapper.getProblemVOList(params);
    }

    @Override
    @Transactional
    public long doProblem(SubmitRequest params) {
        var problemId= params.getProblemId();
        var code = params.getCode();
        var language = params.getLanguage();
        var userId = StpUtil.getLoginIdAsLong();
        validator.validate(problemId, code, language,userId);
        var submit = Submission.builder()
                .problemId(problemId)
                .userId(userId)
                .code(code)
                .language(language)
                .status("waiting")
                .build();
        submissionService.save(submit);
        //todo 最后取消注
        var result = this.mapper.incrementSubmissionCount(problemId);
        ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR,"题目提交次数更新失败");
        var statisticResult = userStatisticsMapper.incrementSubmitCount(userId);
        ThrowUtils.throwIf(!statisticResult, ErrorCode.SYSTEM_ERROR,"用户提交次数更新失败");
        var questionSubmitId = submit.getId();
        CompletableFuture.runAsync(() -> {
            judgeService.doJudge(questionSubmitId,problemId,code,language);
        });
        return questionSubmitId;
    }

    @Override
    public List<JudgeCase> getCase(Long id) {
        validator.validateExitProblem(id);
        var CaseId = testCaseMapper.getByProblemId(id);
        var list = caseService.getById(CaseId).getTestCase();
        ThrowUtils.throwIf(list == null, ErrorCode.SYSTEM_ERROR,"测试样例数据库操作失败");
        return gson.fromJson(list, new com.google.gson.reflect.TypeToken<List<JudgeCase>>(){}.getType());
    }

    @Override
    public ProblemVO get(Long id) {
        validator.validateExitProblem(id);
        return this.mapper.getProblemVO(id);
    }
}
